package edu.csu.smartpark.controller;

import com.alibaba.fastjson.JSONObject;
import edu.csu.smartpark.model.DO.UserDO;
import edu.csu.smartpark.model.DTO.LoginDTO;
import edu.csu.smartpark.model.DTO.RegisterDTO;
import edu.csu.smartpark.model.DTO.UserDTO;
import edu.csu.smartpark.model.VO.UserVO;
import edu.csu.smartpark.model.common.BusinessException;
import edu.csu.smartpark.model.common.MsgEntity;
import edu.csu.smartpark.service.UserService;
import edu.csu.smartpark.util.CodeUtil;
import edu.csu.smartpark.util.CommonUtil;
import edu.csu.smartpark.util.Constants;
import edu.csu.smartpark.util.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.util.*;
import java.util.regex.Pattern;


@RestController
@Slf4j
@RequestMapping("/user")
public class UserController extends BaseController{

    @Autowired
    private UserService userService;

    @Autowired
    private CodeUtil codeUtil;

    @Autowired
    private JwtUtil jwtUtil;


    @PostMapping("/login")
    public MsgEntity login(@RequestBody @Validated LoginDTO loginDTO) throws BusinessException {

        UserDO userDO = userService.getUserByPhone(loginDTO.getPhone());
        if (userDO == null){
            throw new BusinessException("-1", "user is not exist, please register first");
        }

        log.info("User is logging in. Username:{} , Password:{}", loginDTO.getPhone(), loginDTO.getPassword());
        if (loginDTO.getLoginType().equals(Constants.PhonePassword)){
            String encryptPassword = CommonUtil.EncryptByMD5(loginDTO.getPassword());
            if (!userDO.getEncryptPassword().equals(encryptPassword)){
                throw new BusinessException("-2", "username or password is wrong");
            }
        }else {
            // 校验手机验证码是否准确
            String phoneCode = codeUtil.getCode(loginDTO.getPhone(), Constants.PhoneCodeForLogin);
            if (phoneCode == null || !phoneCode.equals(loginDTO.getPassword())){
                throw new BusinessException("-3", "phone code is wrong");
            }
        }

        // 校验用户是否已登录,用户已登录直接返回之前产生的token即可
        Map<Object, Object> token = jwtUtil.getTokenFromCache(userDO.getId());
        if (token != null && token.get("token") != null){
            return new MsgEntity("success", MsgEntity.SUCCESS, token.get("token"));
        }

        // 将新的token存入redis
        Map<Object, Object> newToken = new HashMap<>();
        // 用户信息加密token，包含用户电话，用户角色信息
        String jwtToken = generateSessionID(userDO.getId(),userDO.getRoles());
        newToken.put("token", jwtToken);
        jwtUtil.addTokenToCache(newToken);
        return new MsgEntity("success", MsgEntity.SUCCESS, jwtToken);
    }

    @PostMapping("/register")
    public MsgEntity register(@RequestBody @Validated RegisterDTO registerDTO) throws BusinessException{
        // 校验用户是否已存在
        if (userService.isPhoneExist(registerDTO.getPhone())){
            throw new BusinessException("-1","phone number has been registered");
        }
        // 校验手机验证码是否准确
        String phoneCode = codeUtil.getCode(registerDTO.getPhone(), Constants.PhoneCodeForRegister);
        if (phoneCode == null || !phoneCode.equals(registerDTO.getCode())){
            throw new BusinessException("-2", "phone code is wrong");
        }
        // 校验两次输入的密码是否一致
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())){
            throw new BusinessException("-3","password and confirmPassword are inconsistent");
        }

        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(registerDTO, userDO);
        userDO.setEncryptPassword(CommonUtil.EncryptByMD5(registerDTO.getPassword()));
        List<String> roles = new ArrayList<>();
        roles.add(Constants.CommonUser);
        userDO.setRoles(roles);
        String res = userService.register(userDO);
        if (res.equals("")) {
            throw new BusinessException(BusinessException.REGISTERERROR, "server internal error, register fail");
        }

        // 将新的token存入redis
        Map<Object, Object> newToken = new HashMap<>();
        // 用户信息加密token，包含用户电话，用户角色信息
        String jwtToken = generateSessionID(res,userDO.getRoles());
        newToken.put("token", jwtToken);
        jwtUtil.addTokenToCache(newToken);
        return new MsgEntity("success", MsgEntity.SUCCESS, jwtToken);
    }

    @GetMapping("/logout")
    public MsgEntity logout(HttpServletRequest request){
        String jwtToken = request.getHeader("Authorization");
        // 将用户原来的失效token进行删除
        Claims claims = jwtUtil.decode(jwtToken);
        String userId = (String) claims.get("userId");
        jwtUtil.deleteToken(userId);
        return new MsgEntity("success", MsgEntity.SUCCESS, "log out success");
    }

    @PostMapping("/password/fix")
    public MsgEntity fixPassword(@RequestBody JSONObject params) throws BusinessException{
        String phone = params.getString("phone");
        String oldPassword = params.getString("oldPassword");
        String newPassword = params.getString("newPassword");
        String newConfirmPassword = params.getString("newConfirmPassword");
        if (phone == "" || oldPassword == ""){
            throw new BusinessException(BusinessException.PARAMETERERROR, "phone or password can not be empty");
        }
        UserDO userDO = userService.getUserByPhone(phone);
        if (userDO == null){
            throw new BusinessException(BusinessException.PARAMETERERROR, "phone number is wrong, user is not exist");
        }
        if (!userDO.getEncryptPassword().equals(CommonUtil.EncryptByMD5(oldPassword))){
            return new MsgEntity("Success", MsgEntity.SUCCESS, "password is wrong");
        }
        if (!newPassword.equals(newConfirmPassword)){
            throw new BusinessException(BusinessException.PARAMETERERROR, "password and confirmPassword are inconsistent");
        }
        int res = userService.updatePassword(phone, CommonUtil.EncryptByMD5(newPassword));
        if (res < 0){
            throw new BusinessException(BusinessException.INTERNAL_SERVER_ERROR, "Internal Server error: update password fail");
        }

        // 将用户原来的token从redis中删除
        jwtUtil.deleteToken(userDO.getId());
        return new MsgEntity("success", MsgEntity.SUCCESS, "update password success, please login again");
    }

    @PostMapping("/password/forget")
    public MsgEntity forgetPassword(@RequestBody JSONObject params) throws BusinessException{
        String phone = params.getString("phone");
        String phoneCode = params.getString("code");
        String newPassword = params.getString("newPassword");
        String newConfirmPassword = params.getString("newConfirmPassword");
        if (phone == "" || phoneCode == ""){
            return new MsgEntity("success", MsgEntity.SUCCESS, "phone or verify code can not be empty");
        }
        UserDO userDO = userService.getUserByPhone(phone);
        if (userDO == null){
            return new MsgEntity("success", MsgEntity.SUCCESS, "user is not exist");
        }
        String verifyCode = codeUtil.getCode(phone,Constants.PhoneCodeForForgetPassword);
        if (verifyCode == null || !verifyCode.equals(phoneCode)){
            return new MsgEntity("success", MsgEntity.SUCCESS, "verify code is wrong");
        }
        if (newPassword == null || newConfirmPassword == null){
            return new MsgEntity("success", MsgEntity.SUCCESS, "password or confirmPassword is empty");
        }
        if (!newPassword.equals(newConfirmPassword)){
            return new MsgEntity("success", MsgEntity.SUCCESS, "password and confirmPassword are inconsistent");
        }
        int res = userService.updatePassword(phone, CommonUtil.EncryptByMD5(newPassword));
        if (res < 0){
            return new MsgEntity("success", MsgEntity.SUCCESS, "Internal Server error: update password fail");
        }
        // 将用户原来的失效token从redis中删除
        jwtUtil.deleteToken(userDO.getId());
        return new MsgEntity("success", MsgEntity.SUCCESS, "update password success, please login again");
    }

    @GetMapping("/searchbyphone")
    public MsgEntity searchUserByPhone(@RequestParam("phone") String phone) throws BusinessException{
        if (phone.equals("")){
            return new MsgEntity("fail", MsgEntity.FAIL, "phone number can not be empty");
        }
        UserDO userDO = userService.getUserByPhone(phone);
        if (userDO == null){
            return new MsgEntity("success", MsgEntity.SUCCESS, "user is not exist");
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        return new MsgEntity("success", MsgEntity.SUCCESS, userVO);
    }

    @GetMapping("/get")
    public MsgEntity getUserInfo(HttpServletRequest request) throws BusinessException{
        String jwtToken = request.getHeader("Authorization");
        Claims claims = jwtUtil.decode(jwtToken);
        String userId = (String)claims.get("userId");
        UserDO userDO = userService.getUserById(userId);
        if (userDO == null){
            return new MsgEntity("success", MsgEntity.SUCCESS, null);
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        return new MsgEntity("success", MsgEntity.SUCCESS, userVO);
    }

    @GetMapping("/phonecode")
    public MsgEntity getPhoneCode(String phone, String type){
        if (phone == null || phone.equals("")){
            return new MsgEntity("success", MsgEntity.SUCCESS, "phone can not be empty");
        }
        if (type == null || type.equals("")){
            return new MsgEntity("success", MsgEntity.SUCCESS, "code type can not be empty");
        }
        String phoneRegexp = "^1[0-9]{10}$";
        if (!Pattern.matches(phoneRegexp, phone)){
            return new MsgEntity("success", MsgEntity.SUCCESS, "phone number is not legal");
        }
        String code = codeUtil.sendCode(phone, type);
        return new MsgEntity("success", MsgEntity.SUCCESS, code);
    }

    @GetMapping("/emailcode")
    public MsgEntity getEmailCode(String email){
        if (email == null || email.equals("")){
            return new MsgEntity("success", MsgEntity.SUCCESS, "email can not be empty");
        }
        String code = codeUtil.sendEmailCode(email);
        return new MsgEntity("success", MsgEntity.SUCCESS,code);
    }

    @PostMapping("/avatar")
    public MsgEntity uploadAvatar(String phone, MultipartFile avatar) throws BusinessException {
        if (phone == "" || avatar.isEmpty()){
            return new MsgEntity("success", MsgEntity.SUCCESS, "avatar can not be empty");
        }
        String url = userService.uploadImage(phone, avatar);
        if (url == ""){
            return new MsgEntity("success", MsgEntity.SUCCESS, "avatar upload fail");
        }
        return  new MsgEntity("success", MsgEntity.SUCCESS, url);
    }

    @PostMapping("/info/edit")
    public MsgEntity modifyUserInfo(@RequestBody @Validated UserDTO userDTO) throws BusinessException {
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(userDTO, userDO);
        int id = userService.updateUser(userDO);
        if (id <= 0){
            return new MsgEntity("success", MsgEntity.SUCCESS, "modify user info fail");
        }
        userDO = userService.getUserByPhone(userDO.getPhone());
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        return new MsgEntity("success", MsgEntity.SUCCESS, userVO);
    }

    @PostMapping("/phone/edit")
    public MsgEntity modifyPhone(HttpServletRequest request) throws BusinessException{
        String phone = request.getParameter("phone");
        String code = request.getParameter("code");
        String authorization = request.getHeader("Authorization");
        if (phone == "" || code == ""){
            return new MsgEntity("success", MsgEntity.SUCCESS, "phone or code is empty");
        }
        String phoneCode = codeUtil.getCode(phone, Constants.PhoneCodeForPhoneModification);
        if (phoneCode == null || !phoneCode.equals(code)){
            return new MsgEntity("success", MsgEntity.SUCCESS, "verify code is wrong");
        }
        Claims claims = jwtUtil.decode(authorization);
        String userId = (String)claims.get("userId");
        int id =  userService.updatePhone(userId, phone);
        if (id < 0){
            return new MsgEntity("success", MsgEntity.SUCCESS, "update phone number fail");
        }
        return new MsgEntity("success", MsgEntity.SUCCESS, "phone number modification success");
    }

    @PostMapping("/email/edit")
    public MsgEntity modifyEmail(HttpServletRequest request) throws BusinessException{
        String email = request.getParameter("email");
        String code = request.getParameter("code");
        String authorization = request.getHeader("Authorization");
        if (email == "" || code == ""){
            return new MsgEntity("success", MsgEntity.SUCCESS, "email or code is empty");
        }
        String phoneCode = codeUtil.getEmailCode(email);
        if (phoneCode == null || !phoneCode.equals(code)){
            return new MsgEntity("success", MsgEntity.SUCCESS, "verify code is wrong");
        }
        Claims claims = jwtUtil.decode(authorization);
        String userId = (String)claims.get("userId");
        int id =  userService.updateEmail(userId, email);
        if (id < 0){
            return new MsgEntity("success", MsgEntity.SUCCESS, "update email number fail");
        }
        return new MsgEntity("success", MsgEntity.SUCCESS, "email number modification success");
    }

    /*
    * @Description: 角色授权全部走这个逻辑
    * @Author: LZY
    * @Date: 2021/4/20 17:37
    * @Params:
    * @Return:
    */
    @PostMapping("/enterprise_role/set")
    @RequiresRoles(value = {Constants.FirstLevelOwnerManager, Constants.SecondLevelOwnerManager}, logical = Logical.OR)
    public MsgEntity setUserEnterpriseRoles(@RequestBody JSONObject params) throws BusinessException{
        String role = params.getString("role");
        String authorizedPhone = params.getString("authorizedPhone");
        String userId = getUserId();
        if (!role.equals(Constants.SecondLevelOwnerManager) && !role.equals(Constants.CompanyStaff)){
            return new MsgEntity("fail", "-4", "granting the role is not supported");
        }
        userService.setEnterpriseUserRole(userId, authorizedPhone, role);
        return new MsgEntity("success", MsgEntity.SUCCESS, "granting success");
    }

    /*
    * @Description: 根据用户信息生成ID返回给前端，用这个标识用户状态
    * @Author: LZY
    * @Date: 2021/4/20 15:03
    * @Params:
    * @Return:
    */
    private String generateSessionID(String userId, List<String> roles) {
        Map<String, Object> chaim = new HashMap<>();
        chaim.put("userId", userId);
        chaim.put("role", roles);
        // 登录状态过期时间为1天
        return jwtUtil.encode(userId, 24 * 60 * 60 * 1000, chaim);
    }

    private String encryptPassword(String password){
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            BASE64Encoder base64Encoder = new BASE64Encoder();
            String encryptPassword = base64Encoder.encode(messageDigest.digest(password.getBytes("utf-8")));
            return encryptPassword;
        }catch (Exception e){
            log.error("[smart park server]password encrypt fail");
            return "";
        }
    }
}
